package com.cloud.file.service.impl;

import com.aliyun.oss.common.utils.BinaryUtil;
import com.cloud.common.response.SimpleResponse;
import com.cloud.common.syse.HttpCodeE;
import com.cloud.common.syse.SysRespStatusE;
import com.cloud.file.config.AliOSSConfig;
import com.cloud.file.dao.FileInfoDao;
import com.cloud.file.service.OssService;
import com.cloud.model.file.FileInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
public class OssServiceImpl implements OssService {



    @Autowired
    private AliOSSConfig aliOSSConfig;

    @Autowired
    private FileInfoDao fileInfoDao;

    @Transactional
    @Override
    public SimpleResponse handlerCallbackResult(String requestBody, String autorization, String pubKeyUrl, String queryString, String uri) {
        SimpleResponse resp = new SimpleResponse();
        try {
            if (verifyOSSCallbackRequest(requestBody, autorization, pubKeyUrl, queryString, uri)) {
                Map<String, Object> params = splitParams(requestBody);
                if (!params.isEmpty()) {
                    String fileName = params.get("filename").toString();
                    String fileUrl = aliOSSConfig.getHost() + "/" + fileName;
                    saveFileInfo(params, fileUrl, aliOSSConfig.getBucket());
                    params.put("url", fileUrl);
                }
                return resp.setReturnErrMsgExtData(resp,HttpCodeE.调用成功.value,SysRespStatusE.成功.getDesc(),"上传成功",params);
            } else {
                return resp.setReturnErrMsgExtData(resp,HttpCodeE.回调验证失败.value,SysRespStatusE.失败.getDesc(),"回调验证失败",null);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return resp.setReturnErrMsgExtData(resp,HttpCodeE.回调验证失败.value,SysRespStatusE.失败.getDesc(),"回调验证失败",null);
    }

    private FileInfo saveFileInfo(Map<String, Object> params, String path, String bucket) {
        FileInfo file = new FileInfo();
        file.setFilename(params.get("filename").toString());
        file.setBucket(bucket);
        file.setCreateTime(new Date());
        String height = params.get("height").toString();
        String width = params.get("width").toString();
        String size = params.get("size").toString();
        String mimeType = params.get("mimeType").toString();
        if (StringUtils.isNumeric(size)) {
            file.setSize(Long.valueOf(size));
        }
        if (StringUtils.isNumeric(height)) {
            file.setHeight(Integer.valueOf(height));
        }
        if (StringUtils.isNumeric(width)) {
            file.setWidth(Integer.valueOf(width));
        }
        file.setMimeType(mimeType);
        file.setUrl(path);
        return  fileInfoDao.save(file);
    }

    private Map<String, Object> splitParams(String requestBody) {
        Map<String, Object> params = new HashMap<String, Object>();
        try {
            if (StringUtils.isNotBlank(requestBody)) {
                String values = URLDecoder.decode(requestBody, "UTF-8");
                for (String value : values.split("&")) {
                    if (StringUtils.isNotBlank(value) && value.contains("=")) {
                        String[] arrays = value.split("=");
                        params.put(arrays[0], arrays[1]);
                    }
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return params;
    }


    private boolean verifyOSSCallbackRequest(String requestBody, String autorizationInput, String pubKeyUrl,
                                             String queryString, String uri) throws NumberFormatException, IOException {
        boolean ret = false;
        byte[] authorization = BinaryUtil.fromBase64String(autorizationInput);
        byte[] pubKey = BinaryUtil.fromBase64String(pubKeyUrl);
        String pubKeyAddr = new String(pubKey);
        if (!pubKeyAddr.startsWith("https://gosspublic.alicdn.com/")
                && !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/")) {
            return false;
        }
        String retString = readPublicKey(pubKeyAddr);
        retString = retString.replace("-----BEGIN PUBLIC KEY-----", "");
        retString = retString.replace("-----END PUBLIC KEY-----", "");
        String decodeUri = URLDecoder.decode(uri, "UTF-8");
        String authStr = decodeUri;
        if (queryString != null && !queryString.equals("")) {
            authStr += "?" + queryString;
        }
        authStr += "\n" + requestBody;
        ret = doCheck("/file" + authStr, authorization, retString);
        return ret;
    }

    private String readPublicKey(String pubKeyAddr) {
        BufferedReader in = null;
        StringBuffer sb = new StringBuffer("");
        try {
            URL url = new URL(pubKeyAddr);
            URLConnection conn = url.openConnection();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line = "";
            String NL = System.getProperty("line.separator");
            while ((line = in.readLine()) != null) {
                sb.append(line + NL);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }
        return sb.toString();
    }

    public boolean doCheck(String content, byte[] sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = BinaryUtil.fromBase64String(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            Signature signature = Signature.getInstance("MD5withRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            return signature.verify(sign);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return false;
    }


}
